﻿using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Model;

namespace ConsoleApp.Test
{
#pragma warning disable 0809  //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)'
#pragma warning disable 8500

    unsafe internal class TestSpan
    {
        public static void Test()
        {
            //ReadOnlySpan<char> ret1 = "   ABCD   ".AsSpan().Trim();


            //nuint len = new nuint(100);
            //nuint len2 = (nuint)101;
            //nuint len3 = new nuint(102U);
            //nuint len4 = (nuint)103L;

            //uint value1 = (uint)len;
            //ulong value2 = (ulong)len2;
            //int value3 = (int)len3;
            //long value4 = (long)len4;

            //var arr = new int[6];
            //TestSpanX(arr);


            //var xx = new UIntPtr(100);
            //var xx2 = new UIntPtr((ulong)100);

            //int[] pinnedArr = new int[6];
            //Span<int> span = new Span<int>(pinnedArr);
            //SpanPara(span);

            //UnmanagedMemoryHelper.UnsafeAdpter = new UnsafeAdpter();
            //int[] arr = new int[10];
            //GCHandle handle = GCHandle.Alloc(arr, GCHandleType.Pinned);
            //using (IDisposablerItem item = new DisposablerItem(ref handle, 0))
            //{
            //    int[] pinnedArr = (int[])handle.Target;
            //    Span<int> span = new Span<int>(pinnedArr);
            //    SpanPara(span);

            //    string text = span.ToString();

            //    foreach (var value in span)
            //    {
            //        Console.WriteLine(value);
            //    }
            //}


        }

        private static void TestSpanX(int[] arr)
        {
            for (int i = 0; i < arr.Length; i++)
            {
                arr[i] = i;
            }

            using (var span = new SpanX<int>(arr))
            {
                TestSpanX2(span);
            }

            fixed (int* ptr = arr)
            {
                using (var span = new SpanX<int>(ptr, arr.Length))
                {
                    TestSpanX2(span);
                }
            }
        }

        private static void TestSpanX2(SpanX<int> data)
        {
            for (int i = 0; i < data.Length; i++)
            {
                Console.WriteLine(data[i]);
            }
            data.Dispose();
        }


        private static void TestGetPtr()
        {
            var arr = new int[6];
            int* ptr0;
            fixed (int* p = arr)
            {
                ptr0 = p;
            }
            //int* ptr1 = (int*)Unsafe.AsPointer<int>(ref arr[0]);

            GCHandle handle = GCHandle.Alloc(arr, GCHandleType.Pinned);
            int* ptr2 = (int*)handle.AddrOfPinnedObject();

            fixed (int* ppp = &arr[0])
            {

            }

            //int* ptr3 = (int*)Unsafe.AsPointer<int>(ref arr[0]);

            for (int i = 0; i < arr.Length; i++)
            {
                if (i < 3)
                {
                    arr[i] = i;
                }
                else
                {
                    ptr2[i] = i;
                }
            }

            for (int i = 0; i < arr.Length; i++)
            {
                Console.WriteLine(arr[i]);
            }

            handle.Free();
        }

        private static void SpanPara(Span<int> data)
        {
            for (int i = 0; i < data.Length; i++)
            {
                data[i] = i + 1;
            }


            for (int i = 0; i < data.Length; i++)
            {
                Console.WriteLine(data[i]);
            }
        }
    }

    unsafe public struct SpanX<T> : IDisposable
    {
        /// <summary>
        /// 数据指针
        /// </summary>
        internal readonly T* DataPtr;

        /// <summary>
        /// 数据长度
        /// </summary>
        public readonly int Length;

        /// <summary>
        /// 数据长度
        /// </summary>
        public readonly long LongLength;

        private readonly GCHandle _handle;

        /// <summary>
        /// 构造函数初始化
        /// </summary>
        /// <param name="ptr">数据指针</param>
        /// <param name="length">数据长度</param>
        public SpanX(void* ptr, int length)
        {
            if (length < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(length), "元数个数不能小于0");
            }

            this.DataPtr = (T*)ptr;
            this.Length = length;
            this.LongLength = length;
            //this._handle = GCHandle.Alloc(null);//Target=null,IsAllocated=true
            //this._handle = default(GCHandle); //Target=异常,IsAllocated=false
            this._handle = new GCHandle();//Target=异常,IsAllocated=false
        }


        /// <summary>
        /// 构造函数初始化
        /// </summary>
        /// <param name="array">数据数组,需要外部先通过GCHandle将内存固定起来
        /// T[] arr = ...;
        /// GCHandle handle = GCHandle.Alloc(arr, GCHandleType.Pinned);
        /// T[] pinnedArr = (T[])handle.Target;
        /// </param>
        public SpanX(T[] array)
        {
            if (default(T) == null && array.GetType() != typeof(T[]))
            {
                throw new ArgumentException("�������Ͳ�ƥ��");
            }

            this._handle = GCHandle.Alloc(array, GCHandleType.Pinned);
            this.DataPtr = (T*)this._handle.AddrOfPinnedObject();
            this.Length = array.Length;
            this.LongLength = array.LongLength;
        }

        public void Dispose()
        {
            if (this._handle.IsAllocated && this._handle.Target != null)
            {
                this._handle.Free();
            }
        }


        /// <summary>
        /// 获取或设置数据
        /// </summary>
        /// <param name="index">数据索引</param>
        /// <returns></returns>
        public ref T this[int index]
        {
            get
            {
                if (index < 0 || index >= this.Length)
                {
                    throw new ArgumentOutOfRangeException("index");
                }

                return ref *(this.DataPtr + index);
            }
            //set
            //{
            //    if (index < 0 || index >= this.Length)
            //    {
            //        throw new ArgumentOutOfRangeException("index");
            //    }

            //    *(this.DataPtr + index) = value;
            //}
        }
    }
}
